﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "LineTypeDlg.h"
#include "GbmpNew.h"
#include "ILine3d.h"
#include "IUiView.h"
#include "Vector3d.h"
#include "IDocument.h"
#include "GbmpCommon.h"
#include "StringUtil.h"
#include "IUiDocument.h"
#include "ILineTypeNew.h"
#include "ILineTypeData.h"
#include "IUserTransaction.h"
#include "IElementAttributes.h"
#include "IUiDocumentViewManager.h"

#include <QPainter>
#include <QBoxLayout>
#include <QTableView>
#include <QHeaderView>
#include "GmTitleBar.h"
#include <QItemDelegate>
#include <QStandardItemModel>
#include "GmStyleSheetHelper.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

/// 本文件是界面实现的辅助类，不包含GDMP API的演示内容

using namespace gcmp;

// 使用单线体展示线型文字, 需要把单线体字体yjkeng拷贝到bin\\font下
static const bool s_UsingMonolinePreviewLineTypeFont = false;

const std::wstring s_product = L"product";
const std::wstring s_componentEditor = L"componentEditor";

namespace Sample
{
    bool HasFontInfoInPatternString(const std::wstring& patternString)
    {
        if (patternString.empty()) {
            return false;
        }

        size_t bracketStart = patternString.find_first_of('[');
        size_t bracketEnd = patternString.find_first_of(']');
        if (std::wstring::npos != bracketStart || std::wstring::npos != bracketEnd) {
            return true;
        }
        return false;
    }

    double GetBasicLengthFromPatternString(const std::wstring& patternString)
    {
        DBG_WARN_AND_RETURN_UNLESS(!patternString.empty(), -1, L"为什么数据为空",L"GDMP",L"2024-03-30");

        std::vector<std::wstring> subStrs = StringUtil::SplitString(patternString, L",", true);
        if (subStrs.size() > 2 && subStrs.front() == L"A") {
            bool isOk = false;
            double length = StringUtil::ToNumber<double>(subStrs[1], &isOk);
            if (isOk) {
                return length;
            }
        }
        return -1;
    }

    double CalculateScale(const std::wstring& patternString)
    {
        double scale = -1;
        if (HasFontInfoInPatternString(patternString)) {
            double length = GetBasicLengthFromPatternString(patternString);
            if (length > 0)
            {
                scale = 40 / length;   // 40 为经验值
            }
        }
        return scale;
    }

    IDocument* GetIDocument()
    {
        IUiDocumentViewManager* pUiDocumentViewManager = IUiDocumentViewManager::Get();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiDocumentViewManager, L"pUiDocumentViewManager为空",L"GDMP",L"2024-03-30");
        IUiView* pUIView = pUiDocumentViewManager->GetCurrentUiView();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUIView, L"pUIView为空",L"GDMP",L"2024-03-30");
        IUiDocument* pUIDoc = pUIView->GetUiDocument();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUIDoc, L"pUIDoc为空",L"GDMP",L"2024-03-30");
        return  pUIDoc->GetDbDocument();
    }

    class DrawLineTypeDelegate : public QItemDelegate
    {
    public:
        DrawLineTypeDelegate(QObject *parent = 0)
            : QItemDelegate(parent)
        {
        }

        QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
        {
            return nullptr;
        }

        void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
        {
            QItemDelegate::paint(painter, option, index);

            QPen pen;
            pen.setColor(Qt::black);
            pen.setWidth(1);
            painter->setPen(pen);

            // 构造待离散的预览直线
            double height = option.rect.top() + option.rect.bottom();
            double lineHeight = height * 0.5;
            OwnerPtr<ILine3d> opLineToDiscrete = ILine3d::Create(Vector3d(option.rect.left(), lineHeight, 0.0), Vector3d(option.rect.right(), lineHeight, 0.0));

            // 通过线型名字，获得线型对象
            QModelIndex lineTypeNameIndex = index.sibling(index.row(), 0);
            std::wstring lineTypeName = lineTypeNameIndex.data().toString().toStdWString();
            IDocument* pDocument = GetIDocument();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"pDocument为空",L"GDMP",L"2024-03-30");
            const ILineTypeNew* pLineTypeEle = ILineTypeNew::GetLineTypeByName(pDocument, lineTypeName);
            if (!pLineTypeEle) return;
            const ILineTypeData* pLineType = pLineTypeEle->GetLineTypeData();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pLineType, L"pLineType为空",L"GDMP",L"2024-03-30");
            std::vector<Vector3d> vertexList, pointList, textTriangles;
            if (s_UsingMonolinePreviewLineTypeFont)
            {
                double scale = 10 /*CalculateScale(pLineType->GetPatternString()) */;
                pLineType->Discrete(opLineToDiscrete.get(), scale, vertexList, pointList, textTriangles, L"yjkeng");
            }
            else
            {
                double scale = CalculateScale(pLineType->GetPatternString());
                pLineType->Discrete(opLineToDiscrete.get(), scale, vertexList, pointList, textTriangles, L"");
            }

            if (!vertexList.empty())
            {
                for (int i = 0; i < (int)vertexList.size() - 1;)
                {
                    painter->drawLine(QPointF(vertexList[i].X(), height - vertexList[i].Y()), QPointF(vertexList[i + 1].X(), height - vertexList[i + 1].Y()));
                    i += 2;
                }
            }

            QVector<QPointF> qPointList;
            for each (auto& itr in pointList)
            {
                qPointList.push_back(QPointF(itr.X(), itr.Y()));
            }
            painter->drawPoints(qPointList);

            if (!textTriangles.empty())
            {
                painter->setRenderHint(QPainter::Antialiasing);
                painter->setRenderHint(QPainter::TextAntialiasing);
                for (size_t i = 0; i < textTriangles.size(); i = i + 3)
                {
                    QPainterPath path;
                    path.moveTo(QPointF(textTriangles[i].GetX(), height - textTriangles[i].GetY()));
                    path.lineTo(QPointF(textTriangles[i + 1].GetX(), height - textTriangles[i + 1].GetY()));
                    path.lineTo(QPointF(textTriangles[i + 2].GetX(), height - textTriangles[i + 2].GetY()));
                    path.lineTo(QPointF(textTriangles[i].GetX(), height - textTriangles[i].GetY()));
                    painter->drawPath(path);
                }
            }

        }
    };
    class EditDelegate : public QItemDelegate
    {
    public:
        EditDelegate(QObject *parent = 0)
            : QItemDelegate(parent)
        {

        }

        void setEditorData(QWidget *editor, const QModelIndex &index) const
        {
            // 名字应该都在第一列, 取得重命名前的名字
            QModelIndex nameIndex = index.sibling(index.row(), 0);
            QString oldLineTypeName = index.model()->data(nameIndex, Qt::UserRole/*目前使用UserRole*/).toString();
            IDocument* pDocument = GetIDocument();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"pDocument为空",L"GDMP",L"2024-03-30");
            ILineTypeNew* pLineTypeEle = ILineTypeNew::GetLineTypeByName(pDocument, oldLineTypeName.toStdWString());
            if (!pLineTypeEle)  return;
            const ILineTypeData* oldLineType = pLineTypeEle->GetLineTypeData();
            DBG_WARN_AND_RETURN_VOID_UNLESS(oldLineType, L"oldLineType为空",L"GDMP",L"2024-03-30");
            OwnerPtr<ILineTypeData> opNewLineType = oldLineType->Clone();
            DBG_WARN_AND_RETURN_VOID_UNLESS(opNewLineType, L"ILineTypeData克隆失败",L"GDMP",L"2024-03-30");

            // 新值
            bool valueChanged = false;
            QString value = index.model()->data(index, Qt::DisplayRole).toString();
            if (index.column() == 0) //线型名字
            {
                if (value != oldLineTypeName)
                {
                    opNewLineType->SetName(value.toStdWString());
                    valueChanged = true;
                    //备份线型名字
                    const_cast<QAbstractItemModel*>(index.model())->setData(nameIndex, value, Qt::UserRole);
                }
            }
            else if (index.column() == 2) //线型模式
            {
                if (oldLineType->GetPatternString() != value.toStdWString())
                {
                    opNewLineType->SetPatternString(value.toStdWString());
                    valueChanged = true;
                }
            }

            if (valueChanged)
                pLineTypeEle->SetLineTypeData(TransferOwnership(opNewLineType));

            QItemDelegate::setEditorData(editor, index);
        }
    };

    LineTypeDlg::LineTypeDlg(QWidget *parent/* = nullptr*/)
        : QDialog(nullptr, Qt::FramelessWindowHint | Qt::WindowSystemMenuHint
            | Qt::WindowMinMaxButtonsHint)
    {
        std::wstring styleSheetStr = GmStyleSheetHelper::Instance()->GetCurrentStyleData();
        setStyleSheet(QString::fromStdWString(styleSheetStr));

        this->setWindowTitle(QString::fromStdWString(GBMP_TR(L"线型管理")));

        //添加对象名 Add by gaoyu 2016-06-02
        this->setObjectName(QStringLiteral("LineTypeDlg"));

        m_dlgLayout = NEW_AS_QT_CHILD(QVBoxLayout, this);
        m_dlgLayout->setContentsMargins(0, 0, 0, 10);
        m_dlgLayout->setSpacing(6);
        setLayout(m_dlgLayout);
        GmTitleBar* pTitleBar = NEW_AS_QT_CHILD(GmTitleBar, this);
        pTitleBar->setObjectName(QStringLiteral("gbmp_title"));
        pTitleBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
        pTitleBar->setIconVisible(false);
        pTitleBar->setMaxButtonVisible(false);
        pTitleBar->setMinButtonVisible(false);
        pTitleBar->setText(QString::fromStdWString(GBMP_TR(L"线型管理")));
        this->setProperty("use_gbmp_title", true);
        m_dlgLayout->addWidget(pTitleBar);

        CreateTableView();
        CreateButton();
        connect(pTitleBar, &GmTitleBar::closeRequested, this, &LineTypeDlg::reject);
        connect(pTitleBar, &GmTitleBar::moveRequested, this, &LineTypeDlg::onMoveRequested);
        this->resize(600, 600);

        IDocument* pDoc = GetIDocument();
        if (m_opUserTransaction == nullptr && !IUserTransaction::IsInUserTransaction(pDoc))
        {
            m_opUserTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"修改线型属性"));
        }
    }

    LineTypeDlg::~LineTypeDlg()
    {

    }

    void LineTypeDlg::OnOK()
    {
        QAbstractItemModel* pModel = m_tableView->model();
        int iRowIndex = m_tableView->currentIndex().row();
        if (iRowIndex >= 0)
        {
            m_selectLineTypeName = pModel->data(pModel->index(iRowIndex, 0)).toString();
        }
        else
        {
            m_selectLineTypeName = "";
        }

        if (m_opUserTransaction != nullptr)
        {
            m_opUserTransaction->Commit();
        }
        QDialog::accept();
    }

    void LineTypeDlg::CreateTableView()
    {
        m_tableView = NEW_AS_QT_CHILD(QTableView, this);
        m_tableView->horizontalHeader()->setStretchLastSection(true);

        m_model = NEW_AS_QT_CHILD(QStandardItemModel, m_tableView);
        m_model->setHorizontalHeaderLabels(QStringList() << QString::fromStdWString(GBMP_TR(L"线型名称")) << QString::fromStdWString(GBMP_TR(L"线型样式")) << QString::fromStdWString(GBMP_TR(L"线型描述")));
        m_model->setHeaderData(0, Qt::Horizontal, QString::fromStdWString(GBMP_TR(L"线型名称")), Qt::AccessibleDescriptionRole);
        m_model->setHeaderData(1, Qt::Horizontal, QString::fromStdWString(GBMP_TR(L"线型样式")), Qt::AccessibleDescriptionRole);
        m_model->setHeaderData(2, Qt::Horizontal, QString::fromStdWString(GBMP_TR(L"线型描述")), Qt::AccessibleDescriptionRole);
        m_tableView->setModel(m_model);
        m_tableView->setColumnWidth(0, 100);
        m_tableView->setColumnWidth(1, 200);
        m_tableView->setColumnWidth(2, 100);

        m_tableView->setObjectName(QStringLiteral("LineTypeDlgTableView"));//添加对象名 Add by gaoyu 2016-06-01
        int rowIndex = 0;

        IDocument* pDoc = GetIDocument();
        if (m_opUserTransaction == nullptr && !IUserTransaction::IsInUserTransaction(pDoc))
        {
            m_opUserTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"修改线型属性"));
        }
        std::vector<ILineTypeNew*> lineTypes = ILineTypeNew::GetAllLineTypes(pDoc);
        // 按线型名称进行排序，否则界面出现乱序，日志回放的时候索引对应的线型不同，而造成脚本不过
        SortLineTypesByName(lineTypes);

        for each (auto& itr in lineTypes)
        {
            const IElement* pLineTypeElement = itr->GetOwnerElement();
            DBG_WARN_AND_CONTINUE_UNLESS(pLineTypeElement, L"pLineTypeElement为空",L"GDMP",L"2024-03-30");

            const IElementAttributes* pAttributes = pLineTypeElement->GetAttributes();
            DBG_WARN_AND_CONTINUE_UNLESS(pAttributes, L"pAttributes为空",L"GDMP",L"2024-03-30");

            std::wstring procuct;
            bool hasValue = pAttributes->GetAttributeValueAsString(s_product, procuct);
            if (hasValue && procuct == s_componentEditor)
            {
                continue;
            }

            const ILineTypeData* plnTypeData = itr->GetLineTypeData();
            gcmp::OwnerPtr<QStandardItem> opNameItem = NEW_AS_OWNER_PTR(QStandardItem, QString::fromStdWString(plnTypeData->GetName()));

            // 利用Qt::UserRole，备份线型名字！！！！！重要！！！！
            opNameItem->setData(QString::fromStdWString(plnTypeData->GetName()), Qt::UserRole);

            gcmp::OwnerPtr<QStandardItem> opPatternItem = NEW_AS_OWNER_PTR(QStandardItem, QString::fromStdWString(plnTypeData->GetPatternString()));

            QList<QStandardItem*> list;
            list.insert(list.end(), TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opNameItem));
            list.insert(list.end(), nullptr);
            list.insert(list.end(), TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opPatternItem));

            m_model->appendRow(list);
            m_model->setHeaderData(rowIndex++, Qt::Vertical, QString::fromStdWString(plnTypeData->GetName()), Qt::AccessibleDescriptionRole);
        }

        static DrawLineTypeDelegate drawDelegate;
        m_tableView->setItemDelegateForColumn(1, &drawDelegate);
        m_tableView->verticalHeader()->setVisible(false);
        static EditDelegate editDelegate;
        m_tableView->setItemDelegate(&editDelegate);
        QHBoxLayout* pTabelLayout = NEW_AS_QT_LAYOUT(QHBoxLayout);
        pTabelLayout->addWidget(m_tableView);
        pTabelLayout->setContentsMargins(10, 4, 10, 6);
        m_dlgLayout->addLayout(pTabelLayout);

    }

    void LineTypeDlg::CreateButton()
    {
        gcmp::OwnerPtr<QWidget> opButtonWidget = NEW_AS_OWNER_PTR(QWidget);
        //添加对象名 Add by gaoyu 2016-06-02
        opButtonWidget->setObjectName(QStringLiteral("buttonWidget"));

        gcmp::OwnerPtr<QPushButton> opOKButton = NEW_AS_OWNER_PTR(QPushButton);
        opOKButton->setText(QString::fromStdWString(GBMP_TR(L"确认")));
        opOKButton->setBaseSize(QSize(100, 50));
        //添加对象名 Add by gaoyu 2016-06-02
        opOKButton->setObjectName(QStringLiteral("LineTypeDlgOkButton"));
        connect(opOKButton.get(), SIGNAL(clicked()), this, SLOT(OnOK()));
        gcmp::OwnerPtr<QPushButton> opCancelButton = NEW_AS_OWNER_PTR(QPushButton);
        opCancelButton->setText(QString::fromStdWString(GBMP_TR(L"取消")));
        opCancelButton->setObjectName(QStringLiteral("cancelButton"));
        gcmp::OwnerPtr<QSpacerItem> opSpacer = NEW_AS_OWNER_PTR(QSpacerItem, 20, 20, QSizePolicy::Expanding);
        connect(opCancelButton.get(), SIGNAL(clicked()), this, SLOT(OnCancle()));
        gcmp::OwnerPtr<QHBoxLayout> opButtonLayout = NEW_AS_OWNER_PTR(QHBoxLayout);
        opButtonLayout->setContentsMargins(0, 0, 10, 0);
        opButtonLayout->addItem(TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opSpacer));
        opButtonLayout->addWidget(TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opOKButton));
        opButtonLayout->addWidget(TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opCancelButton));
        opButtonWidget->setLayout(TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opButtonLayout));
        m_dlgLayout->addWidget(TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opButtonWidget));
    }

    void LineTypeDlg::OnCancle()
    {
        if (m_opUserTransaction != nullptr)
        {
            m_opUserTransaction->Rollback();
        }

        QDialog::reject();
    }

    void LineTypeDlg::onMoveRequested(const QPoint& cursorPos, const QPoint& offset)
    {
        Q_UNUSED(cursorPos);
        move(pos() + offset);
    }

    // 按线型名称进行排序
    void LineTypeDlg::SortLineTypesByName(std::vector<ILineTypeNew*>& arrLineTypes) const
    {
        auto CompareLineNameFunc = [](const ILineTypeNew* pVal1, const ILineTypeNew* pVal2) -> bool
        {
            DBG_WARN_AND_RETURN_UNLESS(pVal1, false, L"pVal1为空",L"GDMP",L"2024-03-30");
            DBG_WARN_AND_RETURN_UNLESS(pVal2, true, L"pVal2为空",L"GDMP",L"2024-03-30");
            const ILineTypeData* pData1 = pVal1->GetLineTypeData();
            const ILineTypeData* pData2 = pVal2->GetLineTypeData();
            DBG_WARN_AND_RETURN_UNLESS(pData1, false, L"pData1为空",L"GDMP",L"2024-03-30");
            DBG_WARN_AND_RETURN_UNLESS(pData2, true, L"pData2为空",L"GDMP",L"2024-03-30");

            return pData1->GetName() < pData2->GetName();
        };
        std::sort(arrLineTypes.begin(), arrLineTypes.end(), CompareLineNameFunc);
    }
}

